<script setup lang="ts" name="ResourceLibrary">
import { storeToRefs } from 'pinia'
import { useAppStoreWithOut } from '@/store/modules/app'
import { FileApi, FileType, FolderType, RecycleApi } from '@/api/infra/resource/library'
import useCopy from '@/hooks/web/useCopy'
import { getStrDictOptions, getIntDictOptions } from '@/utils/dict'
import { DICT_TYPE } from '@/enums/dictEnums'
import { checkPermit, computedOperateWidth } from '@/utils/permit'
import { cloneDeep } from 'lodash-es'
import { filesizeFormatter, fileExtList } from '@/utils/file/helper'
import FolderTree from './components/FolderTree.vue'
import ResourceRecycle from './ResourceRecycle.vue'
import FileMoveForm from './components/FileMoveForm.vue'
import FileForm from './components/FileForm.vue'
import ImageForm from './components/ImageForm.vue'
import VideoForm from './components/VideoForm.vue'
import UploadForm from './components/UploadForm.vue'
const appStore = useAppStoreWithOut()
const { systemConfig } = storeToRefs(appStore)

interface FileListType extends FileType {
  checked?: boolean
  isEdit?: boolean
}
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const total = ref(0) // 列表的总页数
const list = ref<FileListType[]>([]) // 表格数据
const folderList = ref<Tree[]>([]) // 树形结构
const queryLoading = ref(false) // 列表加载中：1）列表数据加载；2）查询按钮禁用；3）重置按钮禁用
const queryParams = ref({
  pageIndex: 1,
  pageSize: 20,
  folderId: 0,
  name: undefined,
  sortType: undefined,
  mimetype: undefined
})
const queryFormRef = ref() // 搜索的表单
const tableRef = ref() // 表格实例
const viewType = ref('card') // 视图类型 card | table
const allChecked = ref(false) // 是否全选
const isIndeterminate = ref(false) // 是否半选状态
const checkedAllPage = ref<FileListType[]>([]) // 非当前页已选中数据
const checkedCurrentPage = ref<FileListType[]>([]) // 当前页已选中数据
// 总计已选中个数
const checkedCount = computed(() => {
  return checkedAllPage.value.length + checkedCurrentPage.value.length
})
const folderTreeRef = ref() // 文件夹组件
const ResourceRecycleRef = ref() // 回收站
const FileMoveFormRef = ref() // 资源移动弹窗
const FileFormRef = ref() // 文件资源弹窗
const ImageFormRef = ref() // 图片资源弹窗
const VideoFormRef = ref() // 媒体资源编辑弹窗
const UploadFormRef = ref() // 上传资源弹窗
const breadcrumbList = ref<FolderType[]>([]) // 面包屑
const nameInputRefs = ref<HTMLElement[]>([]) // 行内编辑名称 input 框 DOM 对象
const recycleCount = ref(0) // 回收站资源数量
/** 处理文件夹被点击 */
const handleFolderNodeClick = async (row) => {
  queryParams.value.folderId = row.id
  await getList()
}

/** 监听面包屑改变事件 */
const handleBreadcrumb = (breadcrumb) => {
  breadcrumbList.value = breadcrumb
}

/** 文件夹初始化需执行资源初始化 */
const folderInit = async (v) => {
  folderList.value = v
  await getList()
}

// 更新文件夹数据
const updateFolder = () => {
  folderTreeRef.value.getTree()
}

const init = () => {
  folderTreeRef.value.init()
}

// 文件夹删除事件
const handleFolderRemove = async (v) => {
  console.log(v)
  folderList.value = v.folderList
  if (v.isRefresh) {
    queryParams.value.folderId = v.id
    await getList()
  }
  nextTick(() => {
    if (checkedAllPage.value.length > 0) {
      for (let i = 0; i < checkedAllPage.value.length; i++) {
        const isRemove = v.ids.some(k => k === checkedAllPage.value[i].folderId)
        if (isRemove) {
          checkedAllPage.value.splice(i, 1)
          i--
        }
      }
    }
  })
}

/** 查询列表 */
const getList = async () => {
  queryLoading.value = true
  try {
    if (checkedCurrentPage.value.length > 0) {
      checkedCurrentPage.value.forEach(item => {
        const index = checkedAllPage.value.findIndex(k => k.id === item.id)
        if (index === -1) {
          checkedAllPage.value.push(item)
        }
      })
      if (viewType.value === 'table') {
        tableRef.value.clearSelection()
      }
      checkedCurrentPage.value = []
      updateCheckAllStatus()
    }
    const data = await FileApi.page(queryParams.value)
    list.value = data.list
    list.value.forEach(item => {
      const index = checkedAllPage.value.findIndex(v => {
        return v.id === item.id
      })
      if (index !== -1) {
        checkedAllPage.value.splice(index, 1)
        item.checked = true
        checkedCurrentPage.value.push(item)
        if (viewType.value === 'table') {
          nextTick(() => {
            tableRef.value.clearSelection()
            tableRef.value.toggleRowSelection(item, true)
            updateCheckAllStatus()
          })
        } else {
          updateCheckAllStatus()
        }
      }
    })
    total.value = data.total
  } finally {
    queryLoading.value = false
  }
}

/** 搜索按钮操作 */
const handleQuery = () => {
  queryParams.value.pageIndex = 1
  getList()
}

/** 重置按钮操作 */
const resetQuery = () => {
  queryParams.value.pageIndex = 1
  queryParams.value.pageSize = 20
  queryParams.value.folderId = 0
  queryParams.value.name = undefined
  queryParams.value.sortType = undefined
  queryParams.value.mimetype = undefined
  folderTreeRef.value.resetFolder()
  handleQuery()
}

/** 视图类型切换 */
const viewTypeChange = type => {
  viewType.value = type
  if (type === 'table') {
    nextTick(() => {
      if (checkedCurrentPage.value.length === list.value.length) {
        tableRef.value.toggleAllSelection()
      } else {
        tableRef.value.clearSelection()
        checkedCurrentPage.value.forEach(v => {
          tableRef.value.toggleRowSelection(v, true)
        })
      }
    })
  } else {
    updateCheckAllStatus()
  }
}

/** 表格视图资源选中 */
const handleSelectionChange = (val) => {
  checkedCurrentPage.value = val
  list.value.forEach(v => {
    v.checked = false
    const hasCheck = val.some(item => {
      return item.id === v.id
    })
    if(hasCheck) {
      v.checked = true
    }
  })
  updateCheckAllStatus()
}

/** 表格视图单元格单击事件 */
const handleCellClick = (row, column) => {
  if (column.property !== 'operate') {
    row.checked = !row.checked
    if (row.checked) {
      tableRef.value.toggleRowSelection(row, true)
    } else {
      tableRef.value.toggleRowSelection(row, false)
    }
  }
}

/** 卡片视图资源选中 */
const checkedCard = (item) =>  {
  const hasCheck = checkedCurrentPage.value.some(v => {
    return item.id === v.id
  })
  if (!hasCheck) {
    list.value.forEach(v => {
      if(v.id === item.id) {
        v.checked = true
        checkedCurrentPage.value.push(v)
      }
    })
  } else {
    const index = checkedCurrentPage.value.findIndex(v => {
      return v.id === item.id
    })
    checkedCurrentPage.value.splice(index, 1)
    list.value.forEach(v => {
      if(v.id === item.id) {
        v.checked = false
      }
    })
  }
  updateCheckAllStatus()
}
/** 全选/全不选 */
const checkedAll = () => {
  if (checkedCurrentPage.value.length === list.value.length) {
    list.value.forEach(v => {
      v.checked = false
    })
    checkedCurrentPage.value = []
    if (viewType.value === 'table') {
      tableRef.value.clearSelection()
    }
  } else {
    list.value.forEach(v => {
      v.checked = true
    })
    if (viewType.value === 'table') {
      tableRef.value.clearSelection()
      list.value.forEach(v => {
        tableRef.value.toggleRowSelection(v, true)
      })
    } else {
      checkedCurrentPage.value = cloneDeep(list.value)
    }
  }
  updateCheckAllStatus()
}

/** 更新全选/全不选的状态 */
const updateCheckAllStatus = () => {
  if (checkedCurrentPage.value.length === 0) {
    allChecked.value = false
    isIndeterminate.value = false
  } else if (checkedCurrentPage.value.length === list.value.length) {
    allChecked.value = true
    isIndeterminate.value = false
  } else {
    allChecked.value = false
    isIndeterminate.value = true
  }
}

/** 面包屑点击事件 */
const breadcrumbNodeClick = async(item: FolderType) => {
  folderTreeRef.value.updateFolder(item)
  queryParams.value.folderId = item.id!
  await getList()
}

// 资源编辑弹窗判断
const handleEdit = (v) => {
  console.log(v)
  if (v.mimetype === 'image') {
    ImageFormRef.value.open(v.id)
  } else if (v.mimetype === 'video') {
    VideoFormRef.value.open(v.id)
  } else {
    FileFormRef.value.open(v.id)
  }
}

// 打开回收站弹窗
const openRecycle = () => {
  ResourceRecycleRef.value.open()
}

// 打开上传资源弹窗
const openUpload = () => {
  UploadFormRef.value.open(queryParams.value.folderId, queryParams.value.mimetype)
}

// 多选删除
const handleDeleteChecked = () => {
  let res = cloneDeep(checkedAllPage.value)
  if (checkedCurrentPage.value.length > 0) {
    checkedCurrentPage.value.forEach(item => {
      const index = res.findIndex(k => k.id === item.id)
      if (index === -1) {
        res.push(item)
      }
    })
  }
  if (res.length > 0) {
    const ids: number[] = []
    res.forEach(item => {
      ids.push(item.id!)
    })
    handleDelete(ids, true)
  }
}

/** 删除按钮操作 */
const handleDelete = async (ids: number[], isMultiple: boolean) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    // 发起删除
    await FileApi.delete(ids)
    message.success(t('message.del', 'success'))
    if (isMultiple) {
      checkedCurrentPage.value = []
      checkedAllPage.value = []
      allChecked.value = false
      isIndeterminate.value = false
    }
    // 刷新列表
    await getList()
  } catch { }
}

/** '更多'操作按钮 */
const handleCommand = (command, row) => {
  switch (command) {
    case 'copyLink':
      handleCopyLink(row)
      break
    case 'copyCode':
      handleCopyCode(row)
      break
    case 'move':
      handleMove([row.id], false)
      break
    default:
      break
  }
}

const { copy } = useCopy()
const { clipboardRef, copiedRef } = copy()

/** 复制链接操作 */
const handleCopyLink = (row: FileType) => {
  clipboardRef.value = row.url
  if (unref(copiedRef)) {
    message.success(t('message', 'copySuccess'))
  }
}

/** 复制代码操作 */
const handleCopyCode = (row: FileType) => {
  let res = ''
  if (row.mimetype === 'video') {
    res =
      '<video src="' +
      row.url +
      '" controls poster="' +
      (row.coverImageId ? row.coverImage?.url : '') +
      '" preload="auto" webkit-playsinline="true" playsinline="true" x-webkit-airplay="allow" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" style="object-fit:fill"></video>'
  } else if (row.mimetype === 'audio') {
    res = '<audio src="' + row.url + '" controls preload="auto"></audio>'
  } else if (row.mimetype === 'image') {
    res = '<img src="' + row.url + '" alt="' + (row.alt ? row.alt : row.name) + '" />'
  } else {
    res = '<a target="blank" href="' + row.url + '" title="' + row.name + '" download>' + row.name + '</a>'
  }
  clipboardRef.value = res
  if (unref(copiedRef)) {
    message.success(t('message', 'copySuccess'))
  }
}
// 多选移动
const handleMoveChecked = () => {
  let res = cloneDeep(checkedAllPage.value)
  if (checkedCurrentPage.value.length > 0) {
    checkedCurrentPage.value.forEach(item => {
      const index = res.findIndex(k => k.id === item.id)
      if (index === -1) {
        res.push(item)
      }
    })
  }
  if (res.length > 0) {
    const ids: number[] = []
    res.forEach(item => {
      ids.push(item.id!)
    })
    handleMove(ids, true)
  }
}

/** 资源移动操作 */
const handleMove = (ids: number[], isMultiple: boolean) => {
  FileMoveFormRef.value.open(ids, isMultiple)
}

// 资源移动操作完成后
const handleMoveDone = async (v) => {
  if (v) {
    checkedCurrentPage.value = []
    checkedAllPage.value = []
    allChecked.value = false
    isIndeterminate.value = false
  }
  await getList()
}

// 回收站文件恢复后，会恢复到根目录，因此如果资源库当前在根目录，则需要刷新数据
const handleReturn = async () => {
  if (queryParams.value.folderId === 0) {
    await getList()
  }
  await getRecycleCount()
}

// 定义行内重命名input框DOM对象
const setNameInputRef = el => {
  if (el) {
    nameInputRefs.value.push(el)
  }
}

// 触发行内重命名编辑
const nameClick = v => {
  nameInputRefs.value = []
  list.value.forEach(item => {
    if (item.id === v.id) {
      item.isEdit = true
      nextTick(() => {
        nameInputRefs.value[0].focus()
      })
    }
  })
}

// 行内重命名保存
const handleNameChange = async v => {
  list.value.forEach(item => {
    if (item.id === v.id) {
      item.isEdit = false
      nameInputRefs.value = []
    }
  })
  if (v.name === '') {
    message.error(t('infra.resource.library.rules', 'name'))
    return false
  }
  await FileApi.rename({ id: v.id, name: v.name })
  message.success(t('message', 'updateSuccess'))
}

// 更新资源引用情况
const checkQuote = async () => {
  let res = cloneDeep(checkedAllPage.value)
  if (checkedCurrentPage.value.length > 0) {
    checkedCurrentPage.value.forEach(item => {
      const index = res.findIndex(k => k.id === item.id)
      if (index === -1) {
        res.push(item)
      }
    })
  }
  const ids: number[] = []
  res.forEach(item => {
    ids.push(item.id!)
  })
  await FileApi.quote(ids)
  await getList()
  message.success(t('infra.resource.library', 'quoteUpdateSuccess'))
}

// 获得回收站资源数量
const getRecycleCount = async () => {
  RecycleApi.count().then((data) => {
    recycleCount.value = data
  })
}

// ========== 初始化 =========
onMounted(() => {
  // 首次加载小红点
  getRecycleCount()
  // 轮询刷新小红点
  setInterval(
    () => {
      getRecycleCount()
    },
    1000 * 60 * 2
  )
})
</script>

<template>
  <PageCard direction="row">
    <FolderTree ref="folderTreeRef" @node-click="handleFolderNodeClick" @breadcrumb="handleBreadcrumb" @init="folderInit" @node-remove="handleFolderRemove" />
    <ContentFormTable border>
      <template #header>
        <div class="search-button-pane">
          <div class="layout-type">
            <svg-icon :class="[viewType === 'card' ? 'active' : '']" icon="ri:function-line" @click="viewTypeChange('card')" />
            <svg-icon :class="[viewType === 'table' ? 'active' : '']" icon="ri:align-justify" @click="viewTypeChange('table')" />
          </div>
          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
            <el-form-item>
              <el-input v-model="queryParams.name" clearable :placeholder="t('infra.resource.library.file.rules', 'name')" class="!w-160px" @keyup.enter="handleQuery" />
            </el-form-item>
            <el-form-item>
              <el-select v-model="queryParams.mimetype" :placeholder="t('infra.resource.library.file.rules', 'mimetype')" clearable class="!w-160px" @change="handleQuery">
                <el-option v-for="dict in getStrDictOptions(DICT_TYPE.INFRA_RESOURCE_MIMETYPE)" :key="dict.value" :label="dict.label" :value="dict.value" />
              </el-select>
            </el-form-item>
            <el-form-item>
              <el-select v-model="queryParams.sortType" :placeholder="t('infra.resource.library.file.rules', 'sortType')" clearable class="!w-160px" @change="handleQuery">
                <el-option v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_RESOURCE_SORT_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" />
              </el-select>
            </el-form-item>
            <ElFormItem>
              <el-button type="primary" :disabled="queryLoading" icon="Search" @click="handleQuery">{{ t('action', 'search') }}</el-button>
              <el-button icon="Refresh" :disabled="queryLoading" @click="resetQuery">{{ t('action', 'reset') }}</el-button>
            </ElFormItem>
          </el-form>
          <div class="operate-button">
            <CButton v-if="checkPermit(['infra:resource-library:file:check-quote'])" :disabled="queryLoading" plain preIcon="ri:chat-quote-line" :title="t('infra.resource.library.file', 'quote')" @click="checkQuote" />
            <CButton :disabled="queryLoading" plain type="success" preIcon="ri:upload-cloud-line" :title="t('action', 'upload')" @click="openUpload" />
            <ElBadge v-if="checkPermit(['infra:resource-library:recycle:delete', 'infra:resource-library:recycle:return'])" :is-dot="recycleCount > 0" class="ml-12px">
              <CButton :disabled="queryLoading" plain type="warning" preIcon="ri:recycle-line" :title="t('infra.resource.library.recycle', 'title')" @click="openRecycle" />
            </ElBadge>
          </div>
        </div>
      </template>
      <template #tool>
        <div class="breadcrumb-operate">
          <div class="breadcrumb-pane">
            <el-breadcrumb separator="/">
              <template v-for="(item, index) in breadcrumbList" :key="index">
                <el-breadcrumb-item v-if="index < breadcrumbList.length - 1" @click="breadcrumbNodeClick(item)">{{ item.name }}</el-breadcrumb-item>
                <el-breadcrumb-item v-else class="is-leaf">{{ item.name }}</el-breadcrumb-item>
              </template>
            </el-breadcrumb>
          </div>
          <div :class="['operate-pane', systemConfig.size]">
            <el-checkbox v-model="allChecked" :label="allChecked ? t('check', 'cancel') : t('check', 'all')" :indeterminate="isIndeterminate" :disabled="total === 0" class="!mr-12px" @change="checkedAll" />
            <template v-if="checkedCount > 0">
              <el-text>{{ t('check', 'already') }}</el-text>
              <el-text type="danger" class="!mr-6px">{{ checkedCount }}</el-text>
            </template>
            <div v-if="checkedCount > 0" class="edit-button">
              <template v-if="checkedCurrentPage.length === 1">
                <template v-if="checkPermit(['infra:resource-library:file:update'])">
                  <div class="item-edit" @click="handleEdit(checkedCurrentPage[0])">{{ t('action', 'edit') }}</div>
                  <i class="spacer">|</i>
                </template>
                <el-popover placement="bottom-start" :width="80" trigger="hover">
                  <template #reference>
                    <div class="item-edit">
                      {{ t('action', 'copy') }}
                      <el-icon>
                        <ArrowDown />
                      </el-icon>
                    </div>
                  </template>
                  <div class="copy-type">
                    <div class="item-copy" @click="handleCopyLink(checkedCurrentPage[0])">{{ t('infra.resource.library.file', 'copyLink') }}</div>
                    <div class="item-copy" @click="handleCopyCode(checkedCurrentPage[0])">{{ t('infra.resource.library.file', 'copyCode') }}</div>
                  </div>
                </el-popover>
                <i class="spacer">|</i>
              </template>
              <template v-if="checkPermit(['infra:resource-library:file:delete'])">
                <div class="item-edit" @click="handleDeleteChecked">{{ t('action', 'delete') }}</div>
                <i class="spacer">|</i>
              </template>
              <div v-if="checkPermit(['infra:resource-library:file:move'])" class="item-edit" @click="handleMoveChecked">{{ t('action', 'move') }}</div>
            </div>
          </div>
        </div>
      </template>
      <template #content>
        <div v-if="viewType === 'card'" class="content">
          <el-scrollbar v-if="list.length > 0" ref="scrollRef" class="-mx-10px">
            <el-row :gutter="20">
              <el-col v-for="(item, index) in list" :key="index" :xs="12" :sm="12" :md="8" :lg="4" :xl="3" class="pb-10px">
                <div :class="['item-source', item.checked ? 'is-checked' : '']">
                  <div class="source-card" @click.stop="checkedCard(item)">
                    <div class="item-checkbox"><svg-icon v-if="item.checked" icon="ep:check" /></div>
                    <div v-if="item.quote !== undefined && item.quote > 0" class="item-used">{{ t('infra.resource.library.file', 'isQuote') }}</div>
                    <el-image v-if="item.mimetype === 'image'" :src="item.url" fit="fill" lazy />
                    <svg-icon v-else-if="item.mimetype === 'video'" icon="ci:filetype-video" size="64" />
                    <svg-icon v-else-if="item.mimetype === 'audio'" icon="ci:filetype-audio" size="64" />
                    <template v-else>
                      <svg-icon v-if="item.ext && fileExtList.includes(item.ext)" :icon="'ci:filetype-' + item.ext" size="64" />
                      <svg-icon v-else icon="ci:filetype-unknown" size="64" />
                    </template>
                    <div class="item-operate">
                      <svg-icon v-if="checkPermit(['infra:resource-library:file:update'])" icon="ri:edit-box-line" :title="t('action', 'edit')" @click.stop="handleEdit(item)" />
                      <svg-icon icon="ri:link-m" :title="t('infra.resource.library.file', 'copyLink')" @click.stop="handleCopyLink(item)" />
                      <svg-icon icon="ri:file-code-line" :title="t('infra.resource.library.file', 'copyCode')" @click.stop="handleCopyCode(item)" />
                      <svg-icon v-if="checkPermit(['infra:resource-library:file:delete'])" icon="ri:delete-bin-6-line" :title="t('action', 'delete')" @click.stop="handleDelete([item.id!], false)" />
                    </div>
                    <div v-if="(item.mimetype === 'image' || item.mimetype === 'video') && item.width !== 0 && item.height !== 0" class="item-size">
                      <el-text>{{ item.width }}</el-text>
                      <el-text class="!mx-4px">x</el-text>
                      <el-text>{{ item.height }}</el-text>
                    </div>
                    <span v-if="!item.checked" class="file-byte">{{ filesizeFormatter(item.filesize) }}</span>
                  </div>
                  <div v-if="!item.isEdit" class="source-name" :title="item.name" @click.stop="nameClick(item)">
                    <span>{{ item.name }}</span>
                  </div>
                  <el-input v-else :ref="setNameInputRef" v-model="item.name" clearable :maxlength="50" @blur="handleNameChange(item)" />
                </div>
              </el-col>
              <el-col :xs="12" :sm="12" :md="8" :lg="4" :xl="3" class="pb-10px">
                <div class="item-upload-file" @click="openUpload">
                  <div class="item-file">
                    <svg-icon icon="ep:plus" />
                  </div>
                  <div class="source-name">
                    <span>{{ t('upload', 'file') }}</span>
                  </div>
                </div>
              </el-col>
            </el-row>
          </el-scrollbar>
          <template v-else>
            <el-empty>
              <template #description>
                <p>{{ t('infra.resource.library.file.empty', 'description') }}</p>
              </template>
              <template #default>
                <el-button type="primary" @click="openUpload">{{ t('upload', 'file') }}</el-button>
              </template>
            </el-empty>
          </template>
        </div>
        <div v-else class="content">
          <el-table ref="tableRef" :data="list" row-key="id" border @cell-click="handleCellClick" @selection-change="handleSelectionChange">
            <el-table-column type="selection" width="44" fixed="left" />
            <el-table-column prop="url" :label="t('infra.resource.library.file', 'url')" width="90" align="center">
              <template #default="scope">
                <div class="image-wrap">
                  <el-image v-if="scope.row.mimetype === 'image'" :src="scope.row.url" fit="fill" lazy />
                  <svg-icon v-else-if="scope.row.mimetype === 'video'" icon="ci:filetype-video" size="32" />
                  <svg-icon v-else-if="scope.row.mimetype === 'audio'" icon="ci:filetype-audio" size="32" />
                  <template v-else>
                    <svg-icon v-if="fileExtList.includes(scope.row.ext)" :icon="'ci:filetype-' + scope.row.ext" size="32" />
                    <svg-icon v-else icon="ci:filetype-unknown" size="32" />
                  </template>
                </div>
              </template>
            </el-table-column>
            <el-table-column prop="name" :label="t('infra.resource.library.file', 'name')">
              <template #default="scope">
                <div v-if="!scope.row.isEdit" class="td-p" @click.stop="nameClick(scope.row)">
                  {{ scope.row.name }}
                </div>
                <el-input v-else :ref="setNameInputRef" v-model="scope.row.name" clearable :maxlength="50" @blur="handleNameChange(scope.row)" />
              </template>
            </el-table-column>
            <el-table-column prop="description" :label="t('infra.resource.library.file', 'description')">
              <template #default="scope">
                <div class="td-p">{{ scope.row.description }}</div>
              </template>
            </el-table-column>
            <el-table-column prop="filesize" :label="t('infra.resource.library.file', 'filesize')" sortable align="right" width="120">
              <template #default="scope">
                <div class="td-p">{{ filesizeFormatter(scope.row.filesize) }}</div>
              </template>
            </el-table-column>
            <el-table-column prop="description" :label="t('infra.resource.library.file', 'size')" align="center" width="120">
              <template #default="scope">
                <div v-if="scope.row.mimetype === 'image' || scope.row.mimetype === 'video'" class="td-p">
                  <el-text>{{ scope.row.width }}</el-text>
                  <el-text class="!mx-4px">x</el-text>
                  <el-text>{{ scope.row.height }}</el-text>
                </div>
                <div v-else class="td-p">--</div>
              </template>
            </el-table-column>
            <el-table-column prop="create_time" :label="t('infra.resource.library.file', 'create_time')" width="170" sortable align="center" />
            <el-table-column prop="quote" :label="t('infra.resource.library.file', 'isUsed')" width="110" align="center" sortable>
              <template #default="scope">
                <el-tag :type="scope.row.quote > 0 ? 'success' : 'info'">{{ scope.row.quote > 0 ? t('infra.resource.library.file', 'used') : t('infra.resource.library.file', 'unused') }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="operate" :label="t('common', 'operate')" :width="44 + computedOperateWidth(['infra:resource-library:file:update', 'infra:resource-library:file:delete'])" align="center">
              <template #default="scope">
                <el-button v-if="checkPermit(['infra:resource-library:file:update'])" type="primary" icon="Edit" @click="handleEdit(scope.row)" />
                <el-button v-if="checkPermit(['infra:resource-library:file:delete'])" type="danger" icon="Delete" @click="handleDelete([scope.row.id], false)" />
                <el-dropdown @command="(command) => handleCommand(command, scope.row)">
                  <el-button type="primary" link class="ml-12px"><SvgIcon icon="ri:more-fill" size="26" /></el-button>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item command="copyLink"> {{ t('infra.resource.library.file', 'copyLink') }} </el-dropdown-item>
                      <el-dropdown-item command="copyCode"> {{ t('infra.resource.library.file', 'copyCode') }} </el-dropdown-item>
                      <el-dropdown-item v-if="checkPermit(['infra:resource-library:file:move'])" command="move"> {{ t('action', 'move') }} </el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </template>
            </el-table-column>
            <template #empty>
              <el-empty>
                <template #description>
                  <p>{{ t('infra.resource.library.file.empty', 'description') }}</p>
                </template>
                <template #default>
                  <el-button type="primary" @click="openUpload">{{ t('upload', 'file') }}</el-button>
                </template>
              </el-empty>
            </template>
          </el-table>
        </div>
      </template>
      <template #footer>
        <Pagination v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageIndex" :total="total" :disabled="queryLoading" @pagination="getList" />
      </template>
    </ContentFormTable>
    <!-- 回收站 -->
    <ResourceRecycle ref="ResourceRecycleRef" @return="handleReturn" @delete="getRecycleCount" />
    <!-- 上传内容 -->
    <UploadForm ref="UploadFormRef" :folderList="folderList" @success="getList" @updateFolder="updateFolder" @init="init" />
    <!-- 文件移动 -->
    <FileMoveForm ref="FileMoveFormRef" :folderList="folderList" @success="handleMoveDone" />
    <!-- 编辑图片 -->
    <ImageForm ref="ImageFormRef" @success="getList" />
    <!-- 编辑视频 -->
    <VideoForm ref="VideoFormRef" @success="init" />
    <!-- 编辑其他资源 -->
    <FileForm ref="FileFormRef" @success="getList" />
  </PageCard>
</template>

<style scoped lang="scss">

.search-button-pane {
  display: flex;
  align-items: center;
  padding: 6px 12px;
  background-color: var(--el-bg-color-page);

  .layout-type {
    position: relative;
    padding-right: 4px;
    margin-right: 8px;

    &::after {
      position: absolute;
      top: 6px;
      right: 0;
      bottom: 6px;
      display: block;
      width: 1px;
      background-color: var(--el-border-color);
      content: '';
    }

    .el-icon {
      width: 32px;
      height: 32px;
      margin-right: 6px;
      cursor: pointer;

      &:hover{
        color: var(--el-color-primary);
      }

      &.disabled {
        color: var(--el-text-color-disabled);
        cursor: not-allowed;
      }

      &.active {
        color: var(--el-color-primary);

        &:hover{
          color: var(--el-color-primary-light-3);
        }
      }

      :deep(.svg-icon) {
        font-size: 24px;
      }
    }
  }

  .el-form {
    margin-top: 0;
    margin-right: 0;
    margin-left: 0;
    flex: 1;
  }
}

.breadcrumb-operate {
  display: flex;
  flex-direction: column;
  padding: 6px 0;
  margin-top: calc(0px - var(--el-space) + 6px);
}

.breadcrumb-pane {
  position: relative;
  padding: 0;

  .el-breadcrumb {
    overflow: hidden;
    line-height: 32px;

    .el-breadcrumb__item {
      :deep(.el-breadcrumb__inner) {
        color: var(--el-color-primary);
        cursor: pointer;

        &:hover {
          color: var(--el-color-primary-light-3);
        }
      }

      &:last-child {
        :deep(.el-breadcrumb__inner) {
          color: var(--el-text-color-regular);
          cursor: text;
        }
      }
    }
  }
}

.operate-pane {
  display: flex;
  align-items: center;
  height: var(--el-component-size);

  &.large {
    height: var(--el-component-size-large);
  }

  &.small {
    height: var(--el-component-size-small);
  }

  :deep(.el-checkbox__input.is-checked+.el-checkbox__label) {
    color: var(--el-text-color-regular);
  }

  .el-text{
    line-height: 1;
  }

  .edit-button {
    display: flex;
    align-items: center;
    height: 24px;
    margin-left: 10px;
    border: 1px solid var(--el-border-color);
    border-radius: var(--el-border-radius-base);

    .item-edit {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 22px;
      padding: 0 20px;
      line-height: 22px;
      cursor: pointer;

      &:hover {
        color: var(--el-color-primary);
      }

      .el-icon {
        margin-left: 3px;
      }
    }

    .spacer {
      display: inline-block;
      height: 22px;
      font-style: normal;
      line-height: 22px;
      color: var(--el-border-color);
    }
  }
}

.copy-type {
  margin: -6px -12px;

  .item-copy {
    position: relative;
    height: 32px;
    padding: 0 12px;
    line-height: 32px;
    text-align: center;
    cursor: pointer;

    &:hover {
      color: var(--el-color-primary);
      background-color: var(--el-fill-color-light);
    }

    &.active {
      font-weight: bold;
      color: var(--el-color-primary);
    }
  }
}

.content{
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.el-row{
  margin: 0!important;
}

.item-source {
  float: left;
  width: 100%;
  cursor: pointer;

  &.is-checked {
    .source-card {
      background-color: var(--el-color-primary-light-9);
      border: 1px solid var(--el-color-primary-light-5);

      .item-checkbox {
        top: 8px;
        left: 8px;
        background-color: var(--el-color-primary);
        border: 1px solid var(--el-color-primary);
      }
    }
  }

  &:hover {
    .source-card {
      background-color: var(--el-color-primary-light-9);
      border: 1px solid var(--el-color-primary-light-5);

      .item-checkbox {
        top: 8px;
        left: 8px;
      }

      .item-operate {
        bottom: 0;
      }

      .item-size {
        bottom: -22px;
      }

      .file-byte {
        top: -21px;
      }
    }

    .source-name{
      span{
        color: var(--el-color-primary);
      }
    }
  }

  .source-card {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 150px;
    margin-bottom: 10px;
    overflow: hidden;
    background-color: var(--el-fill-color-light);
    border: 1px solid var(--el-border-color);
    border-radius: var(--el-border-radius-base);

    .item-checkbox {
      position: absolute;
      top: -30px;
      left: -30px;
      z-index: 10;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 20px;
      height: 20px;
      background-color: var(--el-fill-color-light);
      border: 1px solid var(--el-border-color);
      border-radius: 2px;
      transition: all 0.5s;

      .el-icon {
        font-size: 16px !important;
        color: var(--el-color-white);
      }
    }

    .item-used {
      position: absolute;
      top: 0;
      right: 0;
      z-index: 2;
      width: 48px;
      height: 48px;
      font-family: "Microsoft YaHei";
      line-height: 75px;
      color: #fff;
      text-align: center;
      cursor: pointer;
      background: rgba(var(--el-color-danger-rgb), 0.5);
      transform: rotate(45deg) translateY(-34px);
    }

    .item-operate {
      position: absolute;
      bottom: -26px;
      left: 0;
      z-index: 10;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 24px;
      background-color: rgba(0, 0, 0, 0.7);
      transition: bottom 0.5s;

      .el-icon {
        position: relative;
        font-size: 18px !important;
        color: var(--el-color-white);
        flex: 1;
      }

      .el-icon+.el-icon {
        &::before {
          position: absolute;
          top: 3px;
          bottom: 3px;
          left: 0;
          display: block;
          width: 1px;
          background-color: rgba(255, 255, 255, 0.2);
          content: '';
        }
      }
    }

    .item-size {
      position: absolute;
      bottom: 0;
      left: 0;
      z-index: 10;
      display: flex;
      width: 100%;
      height: 21px;
      padding: 0 5px;
      overflow: hidden;
      font-size: 12px;
      line-height: 20px;
      background-color: var(--el-color-success-light-9);
      border-top: 1px solid var(--el-border-color);
      transition: bottom 0.3s;
      align-items: center;
      justify-content: center;

      .el-text{
        font-size: 12px;
        color: var(--el-color-success);
      }
    }

    .file-byte {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 10;
      height: 20px;
      padding: 0 5px;
      overflow: hidden;
      font-size: 12px;
      line-height: 18px;
      color: var(--el-color-info);
      background-color: var(--el-color-info-light-9);
      border-right: 1px solid var(--el-border-color);
      border-bottom: 1px solid var(--el-border-color);
      border-bottom-right-radius: var(--el-border-radius-base);
      transition: all 0.5s;
    }

    .el-image {
      max-width: 128px;
      max-height: 128px;

      :deep(.el-image__inner) {
        max-width: 128px;
        max-height: 128px;
      }
    }
  }

  .source-name {
    width: 100%;
    height: 32px;
    overflow: hidden;
    font-size: 0;
    line-height: 0;
    text-align: center;

    span {
      display: inline-block;
      height: 32px;
      padding: 0 2px;
      font-size: var(--el-font-size-base);
      line-height: 32px;
      color: var(--el-text-color-primary);
    }
  }
}

.item-upload-file {
  float: left;
  width: 100%;
  cursor: pointer;

  &:hover {
    .item-file {
      border-color: var(--el-color-primary);

      .el-icon {
        color: var(--el-color-primary);
      }
    }

    .source-name {
      span {
        color: var(--el-color-primary);
      }
    }
  }

  .item-file {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 150px;
    overflow: hidden;
    border: 1px dashed var(--el-border-color);
    border-radius: var(--el-border-radius-base);

    .el-icon {
      font-size: 72px !important;
      color: var(--el-border-color);
    }
  }

  .source-name {
    width: 100%;
    height: 32px;
    margin-top: 10px;
    overflow: hidden;
    font-size: 0;
    line-height: 0;
    text-align: center;

    span {
      display: inline-block;
      height: 32px;
      padding: 0 2px;
      font-size: var(--el-font-size-base);
      line-height: 32px;
      color: var(--el-text-color-primary);
    }
  }
}

.el-table {
  width: 100%;
  height: 100%;

  .item-checkbox {
    display: flex;
    width: 20px;
    height: 20px;
    cursor: pointer;
    background-color: var(--el-color-white);
    border: 1px solid var(--el-border-color);
    border-radius: 2px;
    align-items: center;
    justify-content: center;

    &.disabled {
      cursor: not-allowed;
      background-color: var(--el-fill-color-light);
      border: 1px solid var(--el-border-color);
    }

    &.active {
      background-color: var(--el-color-primary);
      border: 1px solid var(--el-color-primary);
    }

    .el-icon {
      font-size: 16px !important;
      color: var(--el-color-white);
    }
  }

  .image-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin: 0 auto;
    border: 1px solid var(--el-border-color);

    .el-image {
      max-width: 40px;
      max-height: 40px;
    }

    &.folder {
      border: none;

      .el-image {
        max-width: 32px;
        max-height: 32px;
      }
    }
  }

  .operate-popover {
    display: flex;
    width: 40px;
    height: 40px;
    margin: 0 auto;
    line-height: 30px;
    vertical-align: top;
    cursor: pointer;
    border: 1px solid var(--el-fill-color-blank);
    justify-content: center;
  }

  :deep(.el-scrollbar__view) {
    position: relative;
    min-height: 100%;

    .el-table__empty-block {
      position: absolute;

      .el-table__empty-text {
        line-height: 1.5;
      }
    }
  }
}

.el-table--enable-row-hover :deep(.el-table__body) {
  tr:hover>td.el-table__cell {
    .operate-popover {
      color: var(--el-color-primary);
      border: 1px solid var(--el-color-primary);
    }
  }
}
</style>
